/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
package openbackup.system.base.common.utils;

import lombok.extern.slf4j.Slf4j;
import openbackup.system.base.common.constants.Constants;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

/**
 * 内存缓存工具类实现
 *
 */
@Slf4j
public class MemoryCacheUtil {
    // 缓存存储结构，key 是缓存标识，value 是缓存对象
    private static final Map<String, CacheEntry> cacheMap = new ConcurrentHashMap<>();

    // 用于防止并发重复加载的锁
    private static final Map<String, ReentrantLock> locks = new ConcurrentHashMap<>();

    // 定时任务，每5分钟清理一次缓存
    private static final ScheduledExecutorService SCHEDULED_SERVICE = Executors.newScheduledThreadPool(1);

    // 初始化定时任务，每5分钟清理一次缓存
    static {
        SCHEDULED_SERVICE.scheduleAtFixedRate(MemoryCacheUtil::clearExpiredCache, 5, 5,
            TimeUnit.MINUTES);
    }

    /**
     * 通用缓存方法，支持动态传入缓存过期时间（单位：毫秒）
     *
     * @param cacheKey 缓存的唯一标识（如 "policy_123" 或 "system_job_quota"）
     * @param loader 从数据库加载数据的函数
     * @param expireTime 缓存过期时间（单位：毫秒）
     * @param <T> 返回的数据类型
     * @return 缓存或数据库中获取的数据
     */
    public static <T> T getOrLoad(String cacheKey, Function<String, T> loader, Integer expireTime) {
        // 从缓存中获取
        CacheEntry<T> entry = cacheMap.get(cacheKey);
        if (entry != null && !entry.isExpired()) {
            return entry.value;
        }

        // 如果缓存中没有，加锁防止并发重复加载
        ReentrantLock lock = locks.computeIfAbsent(cacheKey, k -> new ReentrantLock());
        try {
            lock.lock();
            // 再次检查缓存，防止重复加载
            entry = cacheMap.get(cacheKey);
            if (entry != null && !entry.isExpired()) {
                return entry.value;
            }

            // 从数据库加载
            T value = loader.apply(cacheKey);
            if (value != null) {
                // 缓存指定时间后过期
                cacheMap.put(cacheKey, new CacheEntry<>(value, expireTime));
            }
            return value;
        } finally {
            lock.unlock();
        }
    }

    // 清理过期缓存
    private static void clearExpiredCache() {
        cacheMap.forEach((key, entry) -> {
            if (entry.isExpired()) {
                ReentrantLock lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
                try {
                    lock.lock();
                    cacheMap.remove(key);
                } finally {
                    lock.unlock();
                    locks.remove(key);
                }
            }
        });
    }

    /**
     * 缓存条目类
     */
    public static class CacheEntry<T> {
        T value;

        /**
         * 相对过期时间 如：60s
         */
        int relativeExpireTime;

        /**
         * 绝对过期时间 如：例如：System.currentTimeMillis() + 60
         */
        long absoluteExpireTime;

        CacheEntry(T value, Integer relativeExpireTime) {
            this.value = value;
            this.relativeExpireTime = relativeExpireTime;
            this.absoluteExpireTime = System.currentTimeMillis() + relativeExpireTime * 1000;
        }

        boolean isExpired() {
            if (relativeExpireTime == Constants.MEMORY_NEVER_EXPIRE) {
                return false;
            }
            return System.currentTimeMillis() >= absoluteExpireTime;
        }
    }
}
